package cn.wzc.crystal.platform.query.impl;

import cn.wzc.crystal.platform.query.*;
import cn.wzc.crystal.platform.util.CryStringUtil;
import com.github.drinkjava2.jdialects.Dialect;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.NonNull;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
public class DefaultQueryService implements QueryService {


	private final Dialect dialect;
	private final QueryProvider provider;
	private final QueryCompiler compiler;
	private final JdbcTemplate jdbcTemplate;

	private <Dto> QueryPageResult<Dto> queryPage(QuerySql sql, int page, int size, Class<Dto> dtoClass) {
		final String pageSQL = this.pageSql(sql.getSql(), page, size);
		final String countSQL = this.countSql(sql.getSql());
		if (log.isDebugEnabled()) {
			log.debug("[分页查询]查询 SQL: {}", pageSQL.replaceAll("\\n", " "));
			log.debug("[分页查询]查询 参数: {}", Arrays.toString(sql.getArgs()));
		}
		final RowMapper<Dto> rowMapper = getDtoRowMapper(dtoClass);
		final List<Dto> rows = jdbcTemplate.query(pageSQL, rowMapper, sql.getArgs());
		final Long total = jdbcTemplate.queryForObject(countSQL, Long.class, sql.getArgs());
		return QueryPageResult.of(total, rows);
	}

	private <Dto> List<Dto> queryList(QuerySql sql, Class<Dto> dtoClass) {
		if (log.isDebugEnabled()) {
			log.debug("[列表查询]查询 SQL: {}", sql.getSql().replaceAll("\\n", " "));
			log.debug("[列表查询]查询 参数: {}", Arrays.toString(sql.getArgs()));
		}
		final RowMapper<Dto> rowMapper = getDtoRowMapper(dtoClass);
		return jdbcTemplate.query(sql.getSql(), rowMapper, sql.getArgs());
	}

	@Override
	public <Dto> List<Dto> queryList(String name, Map<String, Object> context, Class<Dto> dtoClass) {
		final String sqlTemplate = provider.loadTemplate(name);
		final QuerySql sql = compiler.compile(name, sqlTemplate, context);
		return this.queryList(sql, dtoClass);
	}


	@Override
	public <Dto> QueryPageResult<Dto> queryPage(String name, Map<String, Object> context, int page, int size, Class<Dto> dtoClass) {
		final String sqlTemplate = provider.loadTemplate(name);
		final QuerySql sql = compiler.compile(name, sqlTemplate, context);
		return this.queryPage(sql, page, size, dtoClass);
	}

	private String countSql(String sql) {
		return String.format("select count(1) from (%s) _origin_t", sql);
	}

	private String pageSql(String sql, int page, int size) {
		return dialect.pagin(page, size, sql);
	}

	@SuppressWarnings("unchecked")
	private <Dto> RowMapper<Dto> getDtoRowMapper(Class<Dto> dtoClass) {
		RowMapper<Dto> rowMapper;
		if (Map.class.isAssignableFrom(dtoClass)) {
			rowMapper = (RowMapper<Dto>) new InnerColumnMapRowMapper();
		} else {
			rowMapper = new BeanPropertyRowMapper<>(dtoClass);
		}
		return rowMapper;
	}

	private static class InnerColumnMapRowMapper extends ColumnMapRowMapper {
		@Override
		@NonNull
		protected String getColumnKey(@NonNull String columnName) {
			return CryStringUtil.toCamelCase(columnName);
		}
	}


}
